﻿using ET;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;

public class SceneObjectMgr : Singleton<SceneObjectMgr>, IDisposable
{
    private Dictionary<string, GameObject> prefabDict = new Dictionary<string, GameObject>();
    private Dictionary<string, ObjectPool<SceneObject>> poolDict = new Dictionary<string, ObjectPool<SceneObject>>();
    private Dictionary<string, SceneObject> singletonSceneObjectDict = new Dictionary<string, SceneObject>();
    private Dictionary<long, SceneObject> sceneObjectDict = new Dictionary<long, SceneObject>();

    public async ETTask<SceneObject> CreateEffect(Entity entity, string path, Vector3 position = default, Quaternion rotation = default, Vector3 scale = default, int time = -1, Transform parent = default, string singletonKey = default)
    {
        var sceneObject = await CreateObject(entity, path, position, rotation, scale, parent, singletonKey);
        if (time != -1)
        {
            sceneObject.AddComponent<TimeDispose, int>(time);
        }
        return sceneObject;
    }
    public async ETTask<SceneObject> CreateObject(Entity entity, string path, Transform parent = default, string singletonKey = default)
    {
        return await CreateObject(entity, path, Vector3.zero, Quaternion.identity, Vector3.one, parent, singletonKey);
    }

    public async ETTask<SceneObject> CreateObject(Entity entity, string path, Vector3 position, Transform parent = default, string singletonKey = default)
    {
        return await CreateObject(entity, path, position, Quaternion.identity, Vector3.one, parent, singletonKey);
    }

    public async ETTask<SceneObject> CreateObject(Entity entity, string path, Vector3 position, Quaternion rotation, Vector3 scale, Transform parent = default, string singletonKey = default)
    {
        if (!prefabDict.TryGetValue(path, out GameObject prefab))
        {
            prefab = await ResourceHelper.LoadAsync<GameObject>(path);
            if (prefab == null)
                return default;
            prefabDict[path] = prefab;
        }

        if (!poolDict.TryGetValue(path, out ObjectPool<SceneObject> pool))
        {
            pool = new ObjectPool<SceneObject>(() => entity.Create<SceneObject, string, GameObject, string>(path, GameObject.Instantiate(prefab), singletonKey));
            poolDict[path] = pool;
        }

        if (!string.IsNullOrEmpty(singletonKey))
        {
            singletonSceneObjectDict.TryGetValue(singletonKey, out var _sceneObejct);
            if (_sceneObejct != null)
            {
                _sceneObejct.Dispose();
            }
        }

        var sceneObject = pool.Get();
        if (sceneObject == null)
            throw new Exception($"create {path} fail!");

        (sceneObject as IStart)?.Start();
        sceneObject.SetParent(parent);
        sceneObject.SetPosition(position);
        sceneObject.SetRotation(rotation);
        sceneObject.SetScale(scale);

        sceneObjectDict[sceneObject.instanceId] = sceneObject;

        if (!string.IsNullOrEmpty(singletonKey))
        {
            singletonSceneObjectDict[singletonKey] = sceneObject;
        }

        return sceneObject;
    }

    public SceneObject GetSceneObject(long instanceId)
    {
        sceneObjectDict.TryGetValue(instanceId, out var sceneObject);
        return sceneObject;
    }

    public SceneObject GetSceneObject(string singletonKey)
    {
        singletonSceneObjectDict.TryGetValue(singletonKey, out var sceneObject);
        return sceneObject;
    }

    public void RemoveObject(SceneObject sceneObject)
    {
        if (sceneObject == null)
            return;

        if(sceneObjectDict.ContainsKey(sceneObject.instanceId))
        {
            sceneObjectDict.Remove(sceneObject.instanceId);
            if (!string.IsNullOrEmpty(sceneObject.key))
                singletonSceneObjectDict.Remove(sceneObject.key);

            poolDict.TryGetValue(sceneObject.path, out var pool);
            pool.Release(sceneObject);
            return;
        }
        return;
    }

    public void Dispose()
    {
        prefabDict.Clear();
        foreach (var pool in poolDict.Values)
        {
            pool.Dispose();
        }
        poolDict.Clear();
    }
}